/*****************************************************************************
 * Copyright (C) Stacy Curl. All rights reserved.                            
 *  
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 *****************************************************************************/
package picounit;

import picounit.util.ClassMarker;
import junit.framework.Test;

public class PicoUnit implements PicoUnitAPI {
	private final PicoUnitAPI implementation;

	public PicoUnit(String name) {
		this(name, new ImplementationInstantiator());
	}

	public PicoUnit(String name, Class<?> type) {
		this(name, new ClassMarker(type), new ImplementationInstantiator());
	}

	public PicoUnit(String name, Marker marker) {
		this(name, marker, new ImplementationInstantiator());
	}

	private PicoUnit(String name, ImplementationInstantiator implementationInstantiator) {
		this(name, implementationInstantiator.defaultMarker(), implementationInstantiator);
	}

	private PicoUnit(String name, Marker marker, ImplementationInstantiator implementationInstantiator) {
		this.implementation = implementationInstantiator.createImplementation(name, marker);
	}

	///////////////////////////////////////////////////////////////////////////////////////////////
	// PicoUnitAPI methods
	///////////////////////////////////////////////////////////////////////////////////////////////

	public Test generateJUnitTest() {
		return implementation.generateJUnitTest();
	}
	
	public Test generateJUnitTest(Class<?> startingClass) {
		return implementation.generateJUnitTest(startingClass);
	}

	public Test generateJUnitTest(Class<?> startingClass, TestFilter testFilter) {
		return implementation.generateJUnitTest(startingClass, testFilter);
	}

	public Test generateSingleJUnitTest(Class<?> testClass) {
		return implementation.generateSingleJUnitTest(testClass);
	}
	
	public <B, D extends B> void register(Class<B> type, D instance) {
		this.implementation.register(type, instance);
	}

	public <B, D extends B> void register(Class<B> type, Class<D> implementation) {
		this.implementation.register(type, implementation);
	}

	public void register(Class<?> implementation) {
		this.implementation.register(implementation);
	}
	
	public void register(String key, boolean value) {
		this.implementation.register(key, value);
	}
	
	public void register(String key, byte value) {
		this.implementation.register(key, value);
	}
	
	public void register(String key, char value) {
		this.implementation.register(key, value);
	}
	
	public void register(String key, double value) {
		this.implementation.register(key, value);
	}
	
	public void register(String key, float value) {
		this.implementation.register(key, value);
	}
	
	public void register(String key, int value) {
		this.implementation.register(key, value);
	}
	
	public void register(String key, long value) {
		this.implementation.register(key, value);
	}
	
	public void register(String key, short value) {
		this.implementation.register(key, value);
	}
	
	public void register(String key, String value) {
		this.implementation.register(key, value);
	}
	
	public <B> ImplementationCapturer<B> registerFactory(Class<B> type) {
		return this.implementation.registerFactory(type);
	}

	///////////////////////////////////////////////////////////////////////////////////////////////
	// Implementation
	///////////////////////////////////////////////////////////////////////////////////////////////

	private static class ImplementationInstantiator {
		public static class ClassMarker implements Marker {
			public boolean matches(Class<?> testClass) {
				return picounit.Test.MARKER.matches(testClass) ||
					picounit.annotation.Test.MARKER.matches(testClass);
			}

			public String getNoneFoundMessage() {
				return "No tests found";
			}
		}

		public Marker defaultMarker() {
			return new ClassMarker();
		}
		
		public PicoUnitAPI createImplementation(String name, Marker marker) {
			try {
				return (PicoUnitAPI) implementationClass().getConstructor(String.class, Marker.class)
					.newInstance(name, marker);
			}
			catch (Exception e) {
				throw new PicoUnitException(e);
			}
		}
		
		private Class<?> implementationClass() {
			return getClassProperty("picounit.implementation", "picounit.features.base.PicoUnitImpl");
		}

		private Class<?> getClassProperty(String name, String defaultValue) {
			try {
				return Class.forName(System.getProperty(name, defaultValue));
			}
			catch (ClassNotFoundException classNotFoundException) {
				throw new PicoUnitException(classNotFoundException);
			}
		}
	}
}
